home *** CD-ROM | disk | FTP | other *** search
/ Otherware / Otherware_1_SB_Development.iso / mac / hypercar / xfcn / spttool.cpt / Support Tools eXternals 1.2.5 / card_6056.txt < prev    next >
Text File  |  1990-11-13  |  24KB  |  660 lines

  1. -- card: 6056 from stack: in.5
  2. -- bmap block id: 2653
  3. -- flags: 0000
  4. -- background id: 3858
  5. -- name: FilePath
  6. ----- HyperTalk script -----
  7. on HideObjects
  8.   hide cd btn "Try It!"
  9. end HideObjects
  10.  
  11. on ShowObjects
  12.   show cd btn "Try It!"
  13. end ShowObjects
  14.  
  15.  
  16. -- part 1 (button)
  17. -- low flags: 00
  18. -- high flags: A004
  19. -- rect: left=60 top=177 right=213 bottom=191
  20. -- title width / last selected line: 0
  21. -- icon id / first selected line: 0 / 0
  22. -- text alignment: 1
  23. -- font id: 0
  24. -- text size: 12
  25. -- style flags: 8192
  26. -- line height: 16
  27. -- part name: Try it!
  28. ----- HyperTalk script -----
  29. on mouseUp
  30.   put FilePath("STAK", "Which stack do YOU like?") into chosenOne
  31.   if chosenOne = the value of word 2 of the long name of this stack then
  32.     answer "Good choice!  I would have picked that one too!"
  33.   else
  34.     answer "Sorry, wrong answer!  Try again."
  35.   end if
  36. end mouseUp
  37.  
  38.  
  39.  
  40.  
  41. -- part contents for background part 20
  42. ----- text -----
  43.      FilePath displays a modified Standard File dialog, centered on the HyperCard window, to let the user choose a file.  It returns the full path name of the choosen file, or empty if the CANCEL button is choosen.
  44.  
  45.      In addition to the standard Eject, Drive, Select, and Cancel buttons, the XFCN displays the amount of free space on a volume.  Additionally you may supply a prompt string (in parameter two) which will be placed below the file list.
  46.  
  47.      Calling syntax : FilePath(fileType, <promptString>, <"noDialog:"errorGlobal>)
  48.   FILETYPE: a four character file type signature
  49.   PROMPTSTRING: a text string which will be displayed at the bottom of the dialog.
  50.  
  51.       As with all of our XCMDs and XFCNs, passing a single question mark (FilePath("?") in this case) returns the syntax for the external.  Passing an exclamation mark (FilePath("!")) returns the copyright information.
  52.  
  53. -- part contents for background part 38
  54. ----- text -----
  55. 18/50
  56.  
  57. -- part contents for background part 42
  58. ----- text -----
  59. { FileName() XFCN source listing}
  60. { This is an XFCN that brings up a custom standard file dialog to allow the user to select a filename.  It }
  61. {  places an optional string below the file list, and comes up centered in HC's window, regardless of }
  62. {  which monitor it is on. }
  63. {}
  64. {  Written by:  Anup Murarka    Eric Carlson    }
  65. {        ALINK:  SKEPTIC    ALINK:  cyNic  }
  66. {                  CIS:  76004,3356    }
  67. {}
  68. {        We are part of the Support Tools Development Group,  }
  69. {        Apple Computer, Inc.  }
  70. {}
  71. {        please DO NOT contack Mac DTS for support of this code!  }
  72. {}
  73. {        please DO contact the authors for support of this code!  }
  74. {}
  75. {        Send comments, bug reports, requests to any of the above  }
  76. {        E-mail addresses or to:}
  77. {}
  78. {              (one of us)          }
  79. {              Apple Computer, Inc.    }
  80. {              900 E. Hamilton, Ave.    }
  81. {              Campbell, CA   95008    }
  82. {              M/S 72-L          }
  83. {}
  84. {  Copyright:  ┬⌐ 1989, 1990 by Apple Computer, Inc., all rights reserved.  }
  85. {}
  86. { written by  : Anup Murarka                                        }
  87. { AppleLink  : Skeptic                                            }
  88. { modification history                                             }
  89. {       Date      Initials                  Comments                }
  90. {       ----      ------    ------------------------------------------------------}
  91. {    11/29/89  ec&akm    first written                              }
  92. {    8/14/90        ec      recompiled with new libraries for Modal Dialog update  bug  }
  93. {                      & A/UX correct path construction. Changed version to 1.1  }
  94. {}
  95. unit dummyUnit;
  96.  
  97. interface
  98.  
  99.   uses
  100.     HyperXCMD;
  101.  
  102.   procedure main (paramPtr: XCmdPtr);
  103.  
  104. implementation
  105.  
  106.  
  107.   procedure FileName (paramPtr: XCmdPtr);
  108.   FORWARD;
  109.  
  110.   procedure main (paramPtr: XCmdPtr);
  111.   begin
  112.     FileName(paramPtr);
  113.   end;
  114.  
  115.   const
  116.     kSFSaveDisk = $214;        { Negative of current volume refnum [WORD]  }
  117.     kApplScratch = $00000A78;
  118.     kCurDirStore = $398;        { DirID of current directory [LONG]        }
  119.     DITLSizeDiff = 30;
  120.  
  121.   type
  122.     DITLItem = record
  123.         itmHndl: handle;
  124.         itmRect: rect;
  125.         itmType: SignedByte;
  126.         itmData: SignedByte;     { This is really only the length byte.  Data follows of variable length}
  127. {    itmData is followed by the actual data.  See IM I-427}
  128.       end;
  129.     pDITLItem = ^DITLItem;
  130.     hDITLItem = ^pDITLItem;
  131.  
  132.     ItemList = record
  133.         dlgMaxIndex: integer;
  134.         DITLItems: array[0..0] of DITLItem;
  135.       end;
  136.     pItemList = ^ItemList;
  137.     hItemList = ^pItemList;
  138.  
  139.     integerPtr = ^integer;
  140.  
  141.   procedure reportToUser (paramPtr: XCmdPtr;
  142.                   msgStr: str255);
  143. {}
  144. { report something back to the user.  }
  145. { the last parameter (optional) to an external may contain }
  146.  { "noDialog" or "noDialog:GlobalName".  GlobalName is the name }
  147.  { of a HyperTalk global variable into which error messages will be }
  148.  { placed.  we've decided to use this approach to avoid confusing }
  149. { an error message with a valid result being returned from an XFCN. }
  150. {}
  151.     var
  152.       tempStr: str255;
  153.   begin
  154. {check the last param to see if the user requested that}
  155. { we suppress the error dialog }
  156.     ZeroToPas(paramPtr, paramPtr^.params[paramPtr^.paramCount]^, tempStr);
  157.     UprString(tempStr, true);
  158.     if pos('NODIALOG', tempStr) = 0 then
  159.   { no special error handling specified, throw up a dialog and return the error message }
  160.       begin
  161.         SendCardMessage(paramPtr, concat('answer "', msgStr, '"'));
  162.         paramPtr^.returnValue := PasToZero(paramPtr, msgStr);
  163.       end
  164.     else if (pos(':', tempStr) > 0) then
  165.   { requested global AND noDialog so we fill in the global and return empty }
  166.       begin
  167.         tempStr := copy(tempStr, pos(':', tempStr) + 1, length(tempStr));
  168.                             { get the name of the HC global  to fill }
  169.         SetGlobal(paramPtr, tempStr, PasToZero(paramPtr, msgStr));
  170.                             { and fill it }
  171.         paramPtr^.returnValue := PasToZero(paramPtr, '');  { return empty }
  172.       end
  173.     else
  174.   { requested noDialog only so we return the error condition as the result }
  175.       paramPtr^.returnValue := PasToZero(paramPtr, msgStr);
  176.   end;  { procedure }
  177.  
  178.   function AskedForHelp (paramPtr: XCmdPtr;
  179.                   syntaxMsg: Str255;
  180.                   copyrightMsg: Str255): boolean;
  181. {  check to see if the user sent a '?' or a '!' as }
  182. { the only parameter. if so we will respond with }
  183. { the calling syntax or the copyright/version info }
  184. { for this external }
  185. {}
  186.     var
  187.       firstStr: str255;
  188.   begin
  189.     askedForHelp := false;
  190.     if paramPtr^.paramCount = 1 then
  191.       begin
  192.         ZeroToPas(paramPtr, paramPtr^.params[1]^, firstStr);
  193.           { what is the first param? }
  194.         if firstStr = '?' then
  195.           begin
  196.             reportToUser(paramPtr, syntaxMsg);
  197.             askedForHelp := true
  198.           end  { asked for help }
  199.         else if firstStr = '!' then
  200.           begin
  201.             reportToUser(paramPtr, copyRightMsg);
  202.             askedForHelp := true
  203.           end;  { asked for copyright info }
  204.       end;  { one parameter passed }
  205.   end;  { function }
  206.  
  207.   function PathNameFromDirID (dirID: longint;
  208.                   vRefnum: integer;
  209.                   var fullPathName: str255): OSErr;
  210. { build up a full path name given a directory id and an vol ref num.  this method isn't reccomended in general (see the }
  211. {  various tech notes), but we use it in HC externals as HC uses exclusively full path names }
  212.     var
  213.       myCPB: CInfoPBRec;
  214.       directoryName: str255;
  215.       err: OSErr;
  216.   begin
  217.     fullPathName := '';
  218.     with myCPB do
  219.       begin
  220.         ioNamePtr := @directoryName;
  221.         ioDrParID := DirId;
  222.       end;
  223.  
  224.     repeat
  225.       with myCPB do
  226.         begin
  227.           ioVRefNum := vRefNum;
  228.           ioFDirIndex := -1;
  229.           ioDrDirID := myCPB.ioDrParID;
  230.         end;
  231.       err := PBGetCatInfo(@myCPB, FALSE);
  232.  
  233.       directoryName := concat(directoryName, ':');
  234.  
  235. { pascal strings mustn't be longer than 255 chars, though a path name may, so check }
  236.       if length(directoryName) + length(fullPathName) <= 255 then
  237.         fullPathName := concat(directoryName, fullPathName)
  238.       else
  239.         myCPB.ioDrDirID := fsRtDirID;    { lazy persons way to jump out }
  240.  
  241.     until (myCPB.ioDrDirID = 2);
  242.     PathNameFromDirID := err;
  243.   end;
  244.  
  245.   function StrToRect (paramPtr: XCMDPtr;
  246.                   rectStr: Str255): Rect;
  247. { convert a string, as from a callback or a passed parameter, to a rect }
  248.     var
  249.       where: Integer;
  250.       tempRect: rect;
  251.   begin
  252.     where := POS(',', rectStr);
  253.     tempRect.left := StrToNum(paramPtr, COPY(rectStr, 1, where - 1));
  254.     DELETE(rectStr, 1, where);
  255.  
  256.     where := POS(',', rectStr);
  257.     tempRect.top := StrToNum(paramPtr, COPY(rectStr, 1, where - 1));
  258.     DELETE(rectStr, 1, where);
  259.  
  260.     where := POS(',', rectStr);
  261.     tempRect.right := StrToNum(paramPtr, COPY(rectStr, 1, where - 1));
  262.     DELETE(rectStr, 1, where);
  263.  
  264.     tempRect.bottom := StrToNum(ParamPtr, rectStr);
  265.  
  266.     strToRect := tempRect;
  267.   end;
  268.  
  269.   function HCWindowRect (paramPtr: XCMDPtr): rect;
  270. { the rect of HC's card window, in GLOBAL coordinates }
  271.     var
  272.       theResult: Handle;
  273.       rectStr: str255;
  274.       theLength: INTEGER;
  275.   begin
  276.     rectStr := 'the rect of card window';
  277.     theResult := EvalExpr(paramPtr, rectStr);
  278.     if (theResult <> nil) and (paramPtr^.result = noErr) then
  279.       ZeroToPas(paramPtr, theResult^, rectStr)
  280.     else
  281.       rectStr := '';
  282.     if (theResult <> nil) then
  283.       DisposHandle(theResult);
  284.     HCWindowRect := StrToRect(paramPtr, rectStr);
  285.   end;
  286.  
  287.   function GetScreenSize: rect;
  288.   { we don't have access to quick draw globals, as they lie in HC's global space, but we can }
  289.   { get the monitor size indirectly by checking the portBits field of the window manager port }
  290.   { MacRevealed vol 3, pg 20 }
  291.     var
  292.       deskPort: GrafPtr;
  293.       tempRect: rect;
  294.   begin
  295.     GetWMgrPort(deskPort);    { grab a pointer to the window manager port }
  296.     if deskPort = nil then
  297.       begin
  298.         setRect(tempRect, 0, 0, 512, 342);
  299.         GetScreenSize := tempRect;
  300.       end
  301.     else
  302.       GetScreenSize := deskPort^.portBits.bounds;
  303.   end;
  304.  
  305.   function monitorRect (aPoint: point): rect;
  306.   { given a point, return the rect of the monitor that contains it.}
  307.     const
  308.       SysEnvVersion = 2;
  309.     var
  310.       currGDevice: GDHandle;
  311.       gotTheMonitor: boolean;
  312.       tempRect: rect;
  313.       theSysEnv: SysEnvRec;
  314.       envErr: OSErr;
  315.   begin
  316.     currGDevice := nil;
  317.     envErr := SysEnvirons(SysEnvVersion, theSysEnv);
  318.   {SysEnvirons Version is a constant in the interface section of this file}
  319.     if theSysEnv.hasColorQD then    { only proceed if we have color QD }
  320.       begin
  321.         currGDevice := GetDeviceList;
  322.         gotTheMonitor := false;    { haven't found the monitor yet }
  323.         while (currGDevice <> nil) and not (gotTheMonitor) do
  324.     { we assume that the point is in one of the graphic devices }
  325.           begin
  326.             if PtInRect(aPoint, currGDevice^^.gdRect) then
  327.               begin
  328.                 monitorRect := currGDevice^^.gdRect;
  329.                 gotTheMonitor := true;
  330.               end
  331.             else    { get the next device in the list }
  332.               currGDevice := currGDevice^^.gdNextGD;
  333.           end;
  334.         if currGDevice = nil then
  335.           begin
  336.             setRect(tempRect, 0, 0, 0, 0);
  337.             monitorRect := tempRect;
  338.           end;
  339.       end
  340.     else  {No Color QD}
  341.       begin
  342.         tempRect := GetScreenSize;
  343.         if PtInRect(aPoint, tempRect) then
  344.           monitorRect := tempRect
  345.         else
  346.           begin
  347.             setRect(tempRect, 0, 0, 0, 0);
  348.             monitorRect := tempRect;
  349.           end;
  350.       end;
  351.   end;
  352.  
  353.   function CenterInHCWindow (paramPtr: XCMDPtr;
  354.                   windowRect: rect): point;
  355.     var
  356.       where: point;
  357.       window, screen, tempRect: rect;
  358.       h, v: integer;
  359.   begin
  360.     window := HCWindowRect(paramPtr);    { the rect of card the window }
  361.     screen := monitorRect(window.topLeft);
  362.   { check to see the rect of the monitor containing the upper right corner of the card window }
  363.     setRect(tempRect, 0, 0, 0, 0);
  364.     if EqualRect(screen, tempRect) then
  365.   { if '0,0,0,0' comes back then the upper right is off screen, check the upper left }
  366.       begin
  367.         setPt(where, window.right, window.top);
  368.         screen := monitorRect(where);
  369.       end;
  370.  
  371.     OffsetRect(windowRect, window.left - windowRect.left, window.top - windowRect.top);
  372.   { zero the dlog rect onto the card window }
  373.     h := ((window.right - window.left) - (windowRect.right - windowRect.left)) div 2;
  374.     v := ((window.bottom - window.top) - (windowRect.bottom - windowRect.top)) div 2;
  375.     OffSetRect(windowRect, h, v);
  376.  
  377.   { although it isn't possible to have BOTH upper corners off screen, check for an error. }
  378.   { if we find one, use the default monitor rect }
  379.     if EqualRect(screen, tempRect) then
  380.       screen := GetScreenSize;
  381.  
  382.   { now center the rect in the card window }
  383.     if not (PtInRect(windowRect.topLeft, screen) and PtInRect(windowRect.botRight, screen)) then
  384.       begin    { make sure the dlog rect is fully visible on the screen }
  385.         if windowRect.top < screen.top then
  386.           OffSetRect(windowRect, 0, screen.top - windowRect.top + 10);
  387.         if windowRect.bottom > screen.bottom then
  388.           OffSetRect(windowRect, 0, screen.bottom - windowRect.bottom - 10);
  389.         if windowRect.left < screen.left then
  390.           OffSetRect(windowRect, screen.left - windowRect.left + 10, 0);
  391.         if windowRect.right > screen.right then
  392.           OffSetRect(windowRect, screen.right - windowRect.right - 10, 0);
  393.       end;
  394.     SetPt(where, windowRect.left, windowRect.top);
  395.     CenterInHCWindow := where;
  396.   end;
  397.  
  398.   function unSignedByte (SB: signedByte): integer;
  399.     type
  400.       twoSBAreAnInt = record
  401.           case integer of
  402.             0: (
  403.                 sbArray: array[0..1] of SignedByte
  404.             );
  405.             1: (
  406.                 Int: integer
  407.             );
  408.         end;
  409.     var
  410.       tempInt: twoSBAreAnInt;
  411.   begin
  412.     tempInt.Int := 0;
  413.     tempInt.sbArray[1] := SB;
  414.     unSignedByte := tempInt.int;
  415.   end;
  416.  
  417.   function insertCommas (theNumber: str255): str255;
  418.   { Procedure to insert commas every 3 numeric digits}
  419.     var
  420.       count, group: integer;
  421.   begin
  422.     group := 0;
  423.     for count := length(theNumber) downto 1 do
  424.       begin
  425.         group := group + 1;
  426.         if (group <> 3) or (count = 1) then
  427.           cycle;
  428.         insert(',', theNumber, count);
  429.         group := 0;
  430.       end;
  431.     insertCommas := theNumber;
  432.   end;
  433.  
  434.   procedure drawFreeSpace (theDialog: DialogPtr);
  435.   { draw the amount of free space into the dialog, just above item #5, the eject button }
  436.     var
  437.       thePort: GrafPtr;
  438.       oldFont, oldSize: integer;
  439.       freeSpace: longint;
  440.       freeStr: str255;
  441.       PB: ParamBlockRec;
  442.       strWidth: integer;
  443.       volInfoErr: OSerr;
  444.       eraseArea: rect;
  445.       itemType, left: integer;
  446.       itemHndl: handle;
  447.       itemRect: rect;
  448.   begin
  449.     GetPort(thePort);
  450.     if thePort <> nil then
  451.       begin
  452.         PB.iovRefNum := -(integerPtr(kSFSaveDisk)^);     { grab the VRefNum directly from lo mem}
  453.         PB.ioVolIndex := 0;                         { use vRefNum only }
  454.         PB.ioNamePtr := @freeStr;                   { VERY IMPORTANT!  Tell PBGetVInfo where to }
  455.         volInfoErr := PBGetVInfo(@PB, false);           { put the vol name, even though we don't use it }
  456.  
  457.         if volInfoErr = noErr then
  458.           begin
  459.             FreeSpace := (PB.ioVAlBlkSiz * PB.ioVFrBlk) div 1024;       { Calc the free size}
  460.             NumToString(FreeSpace, FreeStr);
  461.             FreeStr := insertCommas(FreeStr);
  462.           end
  463.         else
  464.           begin
  465.             FreeStr := '????';         { If an error occured, show question marks}
  466.           end;
  467.  
  468.         FreeStr := concat(FreeStr, 'k free');
  469.         oldFont := thePort^.txFont;    { remember the old font }
  470.         oldSize := thePort^.txSize;    { and the size }
  471.         TextFont(3);            { set text to geneva }
  472.         TextSize(9);            { 9 point }
  473.         GetDItem(theDialog, 5, itemType, itemHndl, itemRect);         { Get the coordinates of the Eject button}
  474.         with itemRect do
  475.           setRect(eraseArea, itemRect.left - 5, itemRect.top - 11, itemRect.right + 5, itemRect.top);
  476.         eraseRect(eraseArea);
  477.         strWidth := StringWidth(FreeStr);
  478.         left := ((itemRect.right - itemRect.left) div 2) + itemRect.left;
  479.         MoveTo(left - (strWidth div 2), itemRect.top - 2);  { move the pen}
  480.         DrawString(FreeStr);      { show em how much free space they have... }
  481.         TextFont(oldFont);        { set font to the original }
  482.         TextSize(oldSize);        { and the size }
  483.       end;
  484.   end;
  485.  
  486. {ΓÇóΓÇó PromptedSFGetFile ΓÇóΓÇó}
  487.  
  488.   function getStdDlgFilter (theDialog: DialogPtr;
  489.                   var theEvent: eventRecord;
  490.                   var itemHit: integer): boolean;
  491.   { A dialog filter is usually unneeded for simple std. file stuff.  We use one here so that we can draw the}
  492.   { freespace for the current volume.  The string is drawn not put as a static text item so that we can use a}
  493.   { different font for the string.  Because we draw on an update event we must compensate for std file's bug}
  494.   { which confuses update events meant for windows behind it.  Thus if we see an update event for someone }
  495.   { elses window change the event to a NULL and tell ModalDialog that we've handled it. }
  496.   begin
  497.     getStdDlgFilter := false;  { Pass Standard File package handle all events}
  498.     case theEvent.what of
  499.  
  500.       updateEvt: 
  501.         if DialogPtr(theEvent.message) <> theDialog then
  502.           begin
  503.             itemHit := 100;      { change the event to a NULL }
  504.             getStdDlgFilter := true;  { tell Standard File package that we have handled it}
  505.           end
  506.         else
  507.           drawFreeSpace(theDialog);  { update our free space indicator }
  508.  
  509.       otherwise       { a do nothing case}
  510.     end;  {case}
  511.   end;    {getFileDlgFilter}
  512.  
  513.   function PromptedGetDlgHook (item: Integer;
  514.                   dlg: DialogPtr): Integer;
  515.   { This is the routine that puts the prompt on the dialog}
  516.   { The prompt is added as a static text item to the end of the DITL}
  517.  
  518.     procedure AppendDITL (theDialog: DialogPtr);
  519.       var
  520.         hDITL: hDITLItem;             { Handle to DITL being appended }
  521.         hItems: hItemList;             { Handle to DLOGΓÇÖs item list }
  522.         promptHndl: handle;      { Handle to the prompt }
  523.         itemRect: Rect;
  524.         promptLength: integer;
  525.         error: OSerr;
  526.     begin         { AppendDITL}
  527.       BlockMove(POINTER(kApplScratch), @promptHndl, 4);       { The handle to our prompt was stored in APPLScratch}
  528.       if promptHndl = nil then
  529.         exit(AppendDITL);      { Exit since we don't need to change anything}
  530.       promptLength := integer(GetHandleSize(promptHndl));
  531.  
  532.       SetPort(theDialog);
  533.       with WindowPtr(theDialog)^.portRect do
  534.         SizeWindow(WindowPtr(theDialog), right - left, bottom - top + DITLSizeDiff, TRUE);
  535.     { shift the bottom of the window down for the new item}
  536.  
  537.       hDITL := hDITLItem(NewHandle(SizeOf(DITLItem) + promptLength));
  538.       MoveHHI(handle(hDITL));
  539.       HLock(handle(hDITL));
  540.       SetRect(itemRect, 12, 191, 246, 223);      { rect for the stat text item}
  541.       hDITL^^.itmHndl := promptHndl;
  542.       hDITL^^.itmRect := itemRect;
  543.       hDITL^^.itmType := SignedByte(statText);
  544.       hDITL^^.itmData := SignedByte(promptLength);
  545.       HLock(handle(promptHndl));
  546.   { Copy our prompt onto the end of the DITLrec}
  547.       blockmove(promptHndl^, pointer(ORD4(@hDITL^^.itmData) + 1), promptLength);
  548.       HUnLock(handle(promptHndl));
  549.  
  550.       hItems := hItemList(DialogPeek(theDialog)^.items);
  551.       error := HandAndHand(Handle(hDITL), Handle(hItems));
  552.       hItems^^.dlgMaxIndex := hItems^^.dlgMaxIndex + 1;
  553.  
  554.       HUnlock(Handle(hDITL));
  555.       DisposHandle(Handle(hDITL));
  556.     end;                                          { AppendDITL }
  557.  
  558.   begin
  559.     PromptedGetDlgHook := item;
  560.     if (item = -1) then      { we get a call with -1 before the window is shown }
  561.       AppendDITL(dlg);
  562.   end;
  563.  
  564.   procedure PromptedSFGetFile (pt: point;
  565.                   Prompt: str255;
  566.                   fileFilter: procPtr;
  567.                   typeCount: integer;
  568.                   typeList: SFTypeList;
  569.                   var reply: SFReply);
  570.   { This is the routine that does all of the work.}
  571.     var
  572.       promptHndl: handle;
  573.       savedApplScratch: LongInt;
  574.       DLGHook: ProcPtr;
  575.       oldPort: GrafPtr;
  576.       errorCode: OSErr;
  577.   begin
  578.   { First we need to make sure the prompt is of even length and no longer than 240 chars}
  579.     if length(prompt) > 240 then
  580.       prompt := copy(prompt, 1, 240)
  581.     else if odd(length(prompt)) then
  582.       prompt := concat(prompt, ' ');
  583.  
  584.   { Now we need to save the handle to the prompt where our Dialog Hook routine can find it}
  585.   { If the newString call doesn't work, we save the nil handle anyway so our routine knows it didn't work}
  586.     errorCode := ptrToHand(@prompt[1], promptHndl, length(prompt));
  587.     BlockMove(POINTER(kApplScratch), @savedApplScratch, 4);    { save the appl scratch }
  588.     BlockMove(@promptHndl, POINTER(kApplScratch), 4);        { shove our prompt in }
  589.  
  590.     if promptHndl <> nil then                    { don't need to enlarge DITL if no prompt }
  591.       DLGHook := @PromptedGetDlgHook             { use MyDlgHook }
  592.     else
  593.       DLGHook := ProcPtr(0);                    { effectively, weΓÇÖre passing NIL }
  594.  
  595.     GetPort(oldPort);  { Save anything that we might change}
  596.  
  597.     SFPGetFile(pt, '', fileFilter, typeCount, TypeList, DLGHook, reply, getdlgID, @getStdDlgFilter);
  598.           { have ΓÇÖem pick a file }
  599.  
  600.     SetPort(oldPort);  { and restore anything that we might change}
  601.  
  602.     BlockMove(@savedApplScratch, POINTER(kApplScratch), 4);    { restore the contents of ApplScratch!!!!! }
  603.   end;
  604.  
  605.   procedure FileName (paramPtr: XCMDPtr);
  606.     const
  607.       DITLSizeDiff = 30;    { Room needed for the prompt}
  608.     var
  609.       reply: SFReply;
  610.       pathName, prompt: str255;
  611.       dlogHndl: DialogTHndl;
  612.       tempRect: rect;
  613.       thePt: point;
  614.       theType: SFTypeList;
  615.       typeCount: integer;
  616.       err: OSErr;
  617.   begin
  618.   { First check to see if the user requested syntax or copyright information}
  619.   { If they did, we exit the XFCN.  The subroutine takes care of returning the proper string}
  620.     if askedForHelp(paramPtr, 'FilePath(<fileType>, <promptString>)', 'v1.1, ┬⌐1989, 1990 Apple Computer, Inc. by Anup Murarka & Eric Carlson') then
  621.       exit(FileName);
  622.  
  623.     typeCount := -1;                              { assume they don't want any specific types shown }
  624.     if paramPtr^.paramCount >= 1 then
  625.       begin
  626.         ZeroToPas(paramPtr, paramPtr^.params[1]^, prompt);    { borrow prompt var for file type }
  627.         if prompt <> '' then
  628.           begin
  629.             BlockMove(Ptr(ord4(@prompt) + 1), @theType[0], 4);    { remember the file type }
  630.             typeCount := 1;                                  { show only this type }
  631.           end;
  632.       end;
  633.  
  634.     if paramPtr^.paramCount > 1 then
  635.       ZeroToPas(paramPtr, paramPtr^.params[2]^, prompt)
  636.     else
  637.       prompt := '';          { No default prompt}
  638.  
  639.   { do the calculations to center it in the HC window }
  640.     dlogHndl := DialogTHndl(GetResource('DLOG', getDlgID));
  641.     if dlogHndl <> nil then
  642.       with dlogHndl^^.boundsRect do
  643.         SetRect(tempRect, left, top, right, bottom + DITLSizeDiff)
  644.     else
  645.       SetRect(tempRect, 0, 0, 200, 348);
  646.     thePt := CenterInHCWindow(paramPtr, tempRect);
  647.  
  648.     PromptedSFGetFile(thePt, prompt, nil, typeCount, theType, reply);
  649.   { this routine is in customSF.p and does all of the real work for us.}
  650.     if reply.good then                      { If a file was selected, return the pathname}
  651.       begin
  652.         err := PathNameFromDirID(integerPtr(kCurDirStore)^, reply.vRefNum, pathName);
  653.         if err = noErr then
  654.           begin
  655.             pathName := concat(pathName, reply.fName);
  656.             paramPtr^.returnValue := PasToZero(paramPtr, pathName);
  657.           end;
  658.       end;
  659.   end;
  660. end.